package bb.lanxing.mvp.model;

import android.database.Cursor;
import android.text.TextUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baidubce.util.StringUtils;

import org.json.JSONException;
import org.litepal.LitePal;

import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import bb.lanxing.App;
import bb.lanxing.adapter.mine.HistoryListAdapter;
import bb.lanxing.common.cache.CacheEntity;
import bb.lanxing.common.cache.CacheManager;
import bb.lanxing.common.cache.CacheType;
import bb.lanxing.model.WorkoutDatabaseHelper;
import bb.lanxing.model.database.PacePoint;
import bb.lanxing.model.database.Segment;
import bb.lanxing.model.database.Trackpoint;
import bb.lanxing.model.database.Workout;
import bb.lanxing.model.json.ServerCodeJson;
import bb.lanxing.model.json.ServerJson;
import bb.lanxing.model.json.WorkoutSlopeBean;
import bb.lanxing.model.json.WorkoutSlopePointBean;
import bb.lanxing.mvp.model.i.IHistoryModel;
import bb.lanxing.network.MyHttpClient;
import bb.lanxing.network.NetOnSubscribe;
import bb.lanxing.network.NetSubscribe;
import bb.lanxing.network.Response;
import bb.lanxing.util.thread.ThreadPools;
import bb.lanxing.util.Enums;
import bb.lanxing.util.Log;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Func1;
import rx.schedulers.Schedulers;

public class HistoryModelImpl extends BaseWorkoutModel implements IHistoryModel {

    @Override
    public Observable<List<Integer>> getUserYearCache(final long userId) {
        return Observable.just(userId).subscribeOn(Schedulers.io()).flatMap(l -> {
            List<CacheEntity> cache = CacheManager.getInstance().getCache(CacheType.HistoryYears, userId);
            if (cache != null && !cache.isEmpty()) {
                List<Integer> localWorkoutYears = HistoryModelImpl.this.getLocalWorkoutYears(userId);
                List<Integer> parseArray = JSON.parseArray(cache.get(0).getContent(), Integer.class);
                for (Integer num : localWorkoutYears) {
                    if (!parseArray.contains(num)) {
                        parseArray.add(num);
                    }
                }
                parseArray.sort((num2, num3) -> {
                    if (num3.equals(num2)) {
                        return 0;
                    }
                    return num3 > num2 ? 1 : -1;
                });
                return Observable.just(parseArray);
            }
            return Observable.just(null);
        });
    }

    @Override
    public Observable<List<Integer>> getUserYearByServer(final long userId) {
        return Observable.create(new NetSubscribe(MyHttpClient.requestUserHistoryYears(userId))).subscribeOn(Schedulers.io()).flatMap((Func1<String, Observable<List<Integer>>>) str -> {
            JSONObject parseObject;
            JSONArray parseArray = JSON.parseArray(JSON.parseObject(str, ServerJson.class).getData());
            ArrayList<Integer> arrayList = new ArrayList<>(parseArray.size());
            for (Object next : parseArray) {
                if (next instanceof JSONObject) {
                    parseObject = (JSONObject) next;
                } else {
                    parseObject = JSON.parseObject(next.toString());
                }
                arrayList.add(parseObject.getInteger("year"));
            }
            for (Integer num : HistoryModelImpl.this.getUnUploadWorkoutYearsSync(userId)) {
                if (!arrayList.contains(num)) {
                    arrayList.add(num);
                }
            }
            arrayList.sort((num2, num3) -> {
                if (num3.equals(num2)) {
                    return 0;
                }
                return num3 > num2 ? 1 : -1;
            });
            CacheManager.getInstance().save(CacheType.HistoryYears, userId, JSON.toJSONString(arrayList), true);
            Log.d("user_history", JSON.toJSONString(arrayList));
            return Observable.just(arrayList);
        });
    }

    @Override
    public Observable<List<HistoryListAdapter.MonthDesc>> getMonthData(final int year, final long userId) {
        return Observable.<List<HistoryListAdapter.MonthDesc>>create(subscriber -> {
            Calendar utcCalender = HistoryModelImpl.this.getUtcCalender();
            utcCalender.set(1, year);
            long timeInMillis = utcCalender.getTimeInMillis();
            utcCalender.set(1, year + 1);
            long timeInMillis2 = utcCalender.getTimeInMillis();

            HashMap<String, List<Workout>> workoutByYear = HistoryModelImpl.this.getWorkoutByYear(timeInMillis, timeInMillis2, userId);


            //and starttime>=? and starttime<? and workstatus=32 todo sst, need add back
            Cursor rawQuery = LitePal.getDatabase().rawQuery("select sum(distance),sum(duration),sum(credits),strftime('%m', starttime/1000,'unixepoch','localtime') as month from workout where (userid=? or userid=0) and starttime>=? and starttime<? and workstatus=32  and (locsource<>2 or synchronise=1) group by month order by starttime DESC", new String[]{String.valueOf(userId), String.valueOf(timeInMillis), String.valueOf(timeInMillis2)});//group by month, String.valueOf(timeInMillis), String.valueOf(timeInMillis2)

            int count = rawQuery.getCount();

            List<HistoryListAdapter.MonthDesc> arrayList = new ArrayList<>(count);
            while (rawQuery.moveToNext()) {
                HistoryListAdapter.MonthDesc monthDesc = new HistoryListAdapter.MonthDesc();
                monthDesc.distance = rawQuery.getInt(0);
                monthDesc.duration = rawQuery.getInt(1);
                monthDesc.credits = rawQuery.getDouble(2);
                int month = rawQuery.getInt(3);
                monthDesc.month = String.valueOf(month);
                monthDesc.itemDataSet = new ArrayList<>(workoutByYear.get(monthDesc.month));
                arrayList.add(monthDesc);
            }
            rawQuery.close();
            subscriber.onNext(arrayList);
            subscriber.onCompleted();
        }).subscribeOn(Schedulers.io());
    }

    @Override
    public Observable<HistoryListAdapter.MonthDesc> getMonthInfo(final long userId, final int i, final String str) {
        return Observable.<HistoryListAdapter.MonthDesc>create(subscriber -> {
            HistoryListAdapter.MonthDesc monthDesc;
            Calendar utcCalender = HistoryModelImpl.this.getUtcCalender();
            utcCalender.set(1, i);
            long timeInMillis = utcCalender.getTimeInMillis();
            utcCalender.set(1, i + 1);
            Cursor rawQuery = LitePal.getDatabase().rawQuery("select SUM(distance),SUM(duration),SUM(credits),strftime('%m', starttime/1000,'unixepoch','localtime') as month from workout where (workout.userid=? or workout.userid=0) and starttime>=? and starttime<? and month=? and workstatus=32 and (loc_source<>2 or synchronise=1)", new String[]{String.valueOf(userId), String.valueOf(timeInMillis), String.valueOf(utcCalender.getTimeInMillis()), str});
            if (rawQuery.moveToFirst()) {
                monthDesc = new HistoryListAdapter.MonthDesc();
                monthDesc.distance = rawQuery.getInt(0);
                monthDesc.duration = rawQuery.getInt(1);
                monthDesc.credits = rawQuery.getDouble(2);
                monthDesc.month = rawQuery.getString(3);
            } else {
                monthDesc = null;
            }
            rawQuery.close();
            subscriber.onNext(monthDesc);
            subscriber.onCompleted();
        }).subscribeOn(Schedulers.io());
    }

    private Workout getWorkoutFromJson(org.json.JSONObject j1) {

        int locSourceBiCi = Workout.LOCATION_SOURCE_BICI;//loc source value   todo need and workStatus=32
//        Log.d(TAG, "getWorkoutFromJson: j1=" + j1);
        Workout w3 = new Workout();

        try {

            w3.setDistance(j1.getDouble("distance"));
            w3.setDuration(j1.getLong("duration"));
            w3.setDescription(j1.getString("description"));
            w3.setUserId(j1.getLong("userId"));
            long clientId = j1.getLong("clientId");
            long serverId = j1.getLong("serverId");
            w3.setServerId(serverId);
            w3.setUuid(j1.getString("uuid"));//todo sst
            w3.setCredits(j1.getDouble("credits"));
            w3.setSport(j1.getInt("sport"));
            w3.setStartTime(j1.getLong("startTime"));
            w3.setEndTime(j1.getLong("endTime"));


            w3.setUploadStatus((short) j1.getInt("uploadStatus"));

            w3.setCadenceSource(j1.getInt("cadenceSource"));
            w3.setHeartSource(j1.getInt("heartSource"));
            w3.setLocSource(j1.getInt("locSource"));
            if (w3.getLocSource() == locSourceBiCi) { //
                w3.setSynchronise(true);
            }

//            w3.setAvgCadence(j1.getInt("avgCadence"));//remove duplicated item
            w3.setMatchedSegments(j1.getInt("matchedSegments"));
            w3.setSlopeGradeMax(j1.getString("slopeGradeMax"));
            w3.setThreedWorkout(j1.getString("threedWorkout"));


            w3.setUpDuration(j1.getLong("upDuration"));
            w3.setUpDistance(j1.getDouble("upDistance"));
            w3.setTitle(j1.getString("title"));
            w3.setStep(j1.getInt("step"));
            w3.setStartWheel(j1.getInt("startWheel"));

            w3.setStartCadence(j1.getInt("startCadence"));
            w3.setSlopeGradeMax(j1.getString("slopeGradeMax"));
            w3.setSegmentSport(j1.getString("segmentSport"));
            w3.setSegmentKM(j1.getString("segmentKM"));
            w3.setSegmentIndex(j1.getString("segmentIndex"));
            w3.setSegmentHr(j1.getString("segmentHr"));
            w3.setSegmentCa(j1.getString("segmentCa"));
            w3.setRealtimeSpeed(j1.getDouble("realtimeSpeed"));
            w3.setPowerVI(j1.getDouble("powerVI"));
            w3.setPowerTSS(j1.getDouble("powerTSS"));
            w3.setPowerSource(j1.getInt("powerSource"));
            w3.setPowerNP(j1.getDouble("powerNP"));
            w3.setPowerIF(j1.getDouble("powerIF"));
            w3.setPowerFTP(j1.getDouble("powerFTP"));
            w3.setPointCounts(j1.getLong("pointCounts"));
            w3.setPoi(j1.getString("poi"));
            w3.setPauseDuration(j1.getLong("pauseDuration"));
            w3.setModifyTime(j1.getLong("modifyTime"));
            w3.setMergeRecord(j1.getString("mergeRecord"));
            w3.setMaxWheelRevolution(j1.getInt("maxWheelRevolution"));
            w3.setMaxSpeed(j1.getDouble("maxSpeed"));
            w3.setMaxPower(j1.getDouble("maxPower"));
            w3.setMaxHeartrate(j1.getInt("maxHeartrate"));
            w3.setMaxCadence(j1.getInt("maxCadence"));
            w3.setMatchedSegments(j1.getInt("matchedSegments"));

            w3.setLikeCount(j1.getInt("likeCount"));
            int isLike = j1.getInt("isLike");
            w3.setIsLike(isLike == 1);


            w3.setHeartrate(j1.getInt("heartrate"));
            double grade = j1.getDouble("grade");
            w3.setGrade((float) grade);

            int fullData = j1.getInt("fullData");
            w3.setFullData(fullData > 0);
            w3.setFullDataValue(fullData);//sst shake hand

            w3.setFitPath(j1.getString("fitPath"));
            int export = j1.getInt("isExport");
            w3.setIsExport(export == 1);
            w3.setEquipmentInfo(j1.getString("equipmentInfo"));
            w3.setEndWheel(j1.getInt("endWheel"));
            w3.setEndCadence(j1.getInt("endCadence"));

            w3.setDescription(j1.getString("description"));
            w3.setCreditsDvalue(j1.getDouble("creditsDvalue"));

            w3.setCommentCount(j1.getInt("commentCount"));
            w3.setCategory(j1.getInt("category"));
            w3.setCalorie(j1.getInt("calorie"));

            w3.setCadence(j1.getInt("cadence"));
            w3.setAvgSpeed(j1.getDouble("avgSpeed"));
            w3.setAvgPower(j1.getDouble("avgPower"));
            w3.setAvgHeatKmRate(j1.getString("avgHeatKmRate"));
            w3.setAvgHeatKm(j1.getDouble("avgHeatKm"));
            w3.setAvgHeartrate(j1.getInt("avgHeartrate"));
            w3.setAvgCadence(j1.getInt("avgCadence"));


            w3.setMinGrade(j1.getInt("minGrade"));
            w3.setMaxGrade(j1.getInt("maxGrade"));
            w3.setLastUpload(j1.getLong("lastUpload"));


            w3.setFlatDuration(j1.getLong("flatDuration"));
            w3.setFlatDistance(j1.getDouble("flatDistance"));
            double elevationLoss = j1.getDouble("elevationLoss");
            double elevationGain = j1.getDouble("elevationGain");
            w3.setElevationLoss(((float) elevationLoss));
            w3.setElevationGain((float) elevationGain);

            w3.setDownDuration(j1.getLong("downDuration"));
            w3.setDownDistance(j1.getDouble("downDistance"));

            w3.setWorkStatus(32);//workstatus  please check STATUS_STOPED above
        } catch (Exception e) {
            e.printStackTrace();
        }

        return w3;
    }


    @Override
    public Observable<List<HistoryListAdapter.MonthDesc>> requestWorkout(final int year, final long userId, final HashMap<String, List<Workout>> localData) {
        return Observable.create(new NetSubscribe(MyHttpClient.downloadWorkouts(year, userId))).subscribeOn(Schedulers.io()).flatMap((Func1<String, Observable<List<HistoryListAdapter.MonthDesc>>>) str3 -> {
            long endTime;
            Calendar utcCalender = HistoryModelImpl.this.getUtcCalender();
            boolean isMine = true;
            utcCalender.set(Calendar.YEAR, year);
            long timeInMillis = utcCalender.getTimeInMillis();//2024-1-1 0:0:0
            utcCalender.set(Calendar.YEAR, year + 1);
            long timeInMillis2 = utcCalender.getTimeInMillis();//2025-1-1 0:0:0
            endTime = timeInMillis2;

            HashMap<String, Workout> hashMap = new HashMap<>();
//                HashMap hashMap2 = localData;


            //本地数据放入到hashMap中
            if (localData == null) {
                List<Workout> localList = LitePal.where("(userId=0 or userId=?) and startTime>=? and startTime<?  and (locSource<>2 or synchronise=1)", String.valueOf(userId), String.valueOf(timeInMillis), String.valueOf(timeInMillis2)).find(Workout.class);
                for (Workout workout : localList) {
                    hashMap.put(workout.getUuid(), workout);
                }
            } else {
                for (List<Workout> obj : localData.values()) {
                    for (Workout w2 : obj) {
                        hashMap.put(w2.getUuid(), w2);
                    }

                }

            }

            //hashMap3和arrayList的作用, 存放服务器和本地的workout数据， 合并之后的结果
            DecimalFormat decimalFormat = new DecimalFormat("00");
            HashMap<String, HistoryListAdapter.MonthDesc> hashMap3 = new HashMap<>();
            ArrayList<HistoryListAdapter.MonthDesc> arrayList = new ArrayList<>();

            //解析服务器来的数据
            try {
                org.json.JSONObject jSONObject = new org.json.JSONObject(str3);
                int code = jSONObject.getInt("code");
                if (code != 200) {
                    String msg = jSONObject.getString("msg");
                    App.getContext().showMessage(msg);
                    return null;
                }

                int total = jSONObject.getInt("total");
                org.json.JSONArray jsonArray = jSONObject.getJSONArray("rows");

                List<HistoryListAdapter.MonthDesc> ll = new ArrayList<>();

//                    for (int i2=0; i2<1; i2++){//todo sst will remove this outside circle. only return workout list
//                        jSONObject = jsonArray.getJSONObject(i2);
//
//                        HistoryListAdapter.MonthDesc monthDesc = new HistoryListAdapter.MonthDesc();
//                        monthDesc.month = jSONObject.getString("month");
//                        monthDesc.distance = jSONObject.getLong("distance");
//                        monthDesc.duration = jSONObject.getLong("duration");
//                        monthDesc.credits = jSONObject.getDouble("credits");

//                        org.json.JSONArray itemData = jSONObject.getJSONArray("itemDataSet");

//                        monthDesc.itemDataSet = new ArrayList<>();

                int itemDataSize = jsonArray.length();
//                boolean showExperimental = SharedManager.getInstance().getShowExperimentalFromPreference();
//                if (!showExperimental) {
//                        itemDataSize = 0;
//                }

                for (int j2 = 0; j2 < itemDataSize; j2++) {

                    org.json.JSONObject j1 = jsonArray.getJSONObject(j2);

//                            Workout w = new Workout(j1, isMine);//w comes from server json

                    Workout w3 = getWorkoutFromJson(j1);


                    //把本地和服务器数据重复的部分从hashMap中删除
                    Workout workout4 = (Workout) hashMap.remove(w3.getUuid());


                    utcCalender.setTimeInMillis(w3.getStartTime());
                    String month = decimalFormat.format(utcCalender.get(Calendar.MONTH) + 1);
                    HistoryModelImpl.this.insertMonthWorkout(month, hashMap3, arrayList, w3);


                }


                //本地没上传过的数据插入HistoryModelImpl
                ArrayList<Workout> arrayList22 = new ArrayList<>(hashMap.values());
                for (Object w5 : hashMap.values()) {
                    Workout workout5 = (Workout) w5;
                    if (workout5.getUploadStatus() != Enums.UploadStatus.Uploaded) {
                        arrayList22.remove(workout5);
                        utcCalender.setTimeInMillis(workout5.getStartTime());
                        HistoryModelImpl.this.insertMonthWorkout(decimalFormat.format(utcCalender.get(Calendar.MONTH) + 1), hashMap3, arrayList, workout5);
                    }
                }

                //本地已上传的数据保存
                HistoryModelImpl.this.saveNewWorkout(userId, timeInMillis, endTime, hashMap3, arrayList22);


                //排序
                HistoryModelImpl.this.sortMonthData(arrayList);//最终显示的数据

                return Observable.just(arrayList);


            } catch (Exception e) {
                return null;
            }


        });
    }

    public void insertMonthWorkout(String str, HashMap<String, HistoryListAdapter.MonthDesc> hashMap, List<HistoryListAdapter.MonthDesc> list, Workout workout) {
        HistoryListAdapter.MonthDesc monthDesc = hashMap.get(str);
        if (monthDesc == null) {
            monthDesc = new HistoryListAdapter.MonthDesc();
            monthDesc.month = str;//scope is 1 to 12, not 0 to 11
            hashMap.put(str, monthDesc);
            monthDesc.itemDataSet = new ArrayList();

            list.add(monthDesc);
        }
        monthDesc.duration += workout.getDuration();
        monthDesc.distance = (long) (monthDesc.distance + workout.getDistance());
        monthDesc.credits += workout.getCredits();
        monthDesc.itemDataSet.add(workout);
    }

    public void saveNewWorkout(final long userId, final long minTime, final long maxTime, final HashMap<String, HistoryListAdapter.MonthDesc> hashMap, final ArrayList<Workout> arrayList) {
        ThreadPools.getInstance().addTask(new Runnable() {
            @Override
            public void run() {

                try {
                    LitePal.beginTransaction();

                    Iterator it = arrayList.iterator();
                    while (it.hasNext()) {
                        ((Workout) it.next()).delete();
                    }
                    HashMap hashMap2 = new HashMap();
                    Cursor rawQuery = LitePal.getDatabase().rawQuery("select id, uuid from workout where workout.userid=? and starttime>=? and starttime<?", new String[]{String.valueOf(userId), String.valueOf(minTime), String.valueOf(maxTime)});
                    while (rawQuery.moveToNext()) {
                        hashMap2.put(rawQuery.getString(1), rawQuery.getLong(0));
                    }
                    rawQuery.close();
                    for (HistoryListAdapter.MonthDesc monthDesc : hashMap.values()) {
                        for (Workout workout : monthDesc.itemDataSet) {
                            Long l = (Long) hashMap2.get(workout.getUuid());
                            if (l != null) {
                                workout.setId(l);
//                                LitePal.update(Workout.class, workout.toContentValues(), l);
                            } else {
                                workout.setId(null);
                            }

                            workout.save();
                            //WorkoutDatabaseHelper.save(workout);


                        }
                    }

                    LitePal.setTransactionSuccessful();
                } catch (Exception e) {
                    e.printStackTrace();

                } finally {
                    LitePal.endTransaction();
                }

//                SugarTransactionHelper.doInTansaction(new SugarTransactionHelper.Callback() {
//                    @Override
//                    public void manipulateInTransaction() {
//                        Iterator it = arrayList.iterator();
//                        while (it.hasNext()) {
//                            ((Workout) it.next()).delete();
//                        }
//                        HashMap hashMap2 = new HashMap();
//                        Cursor rawQuery = LitePal.getDatabase().rawQuery("select id, uuid from workout where workout.USER_ID=? and START_TIME>=? and START_TIME<?", new String[]{String.valueOf(j), String.valueOf(j2), String.valueOf(j3)});
//                        while (rawQuery.moveToNext()) {
//                            hashMap2.put(rawQuery.getString(1), rawQuery.getLong(0));
//                        }
//                        rawQuery.close();
//                        for (HistoryListAdapter.MonthDesc monthDesc : hashMap.values()) {
//                            for (Workout workout : monthDesc.itemDataSet) {
//                                Long l = (Long) hashMap2.get(workout.getUuid());
//                                if (l != null) {
//                                    workout.setId(l);
//                                }
//                                WorkoutDatabaseHelper.save(workout);
//                            }
//                        }
//                    }
//                });


            }
        });
    }

    public void sortMonthData(List<HistoryListAdapter.MonthDesc> list) {
        list.sort((monthDesc, monthDesc2) -> Integer.parseInt(monthDesc2.month) - Integer.parseInt(monthDesc.month));
        Comparator<Workout> comparator = (workout, workout2) -> {
            if (workout2.getStartTime() == workout.getStartTime()) {
                return 0;
            }
            return workout2.getStartTime() > workout.getStartTime() ? 1 : -1;
        };
        for (HistoryListAdapter.MonthDesc monthDesc : list) {
            monthDesc.itemDataSet.sort(comparator);
        }
    }

    @Override
    public Observable<Workout> requestWorkoutDetail(final Workout workout) {
        return Observable.create(new NetSubscribe(MyHttpClient.segmentWorkout(workout.getServerId())))
                .subscribeOn(Schedulers.io()).flatMap(str -> {
                    try {
                        String dataStr = JSON.parseObject(str).getString("data");
                        String workoutStr = JSON.parseObject(dataStr).getString("workout");
                        org.json.JSONObject obj = new org.json.JSONObject(workoutStr);
                        Workout workout2 = new Workout(obj, true);

                        String pacePointStr = JSON.parseObject(dataStr).getString("pacePoints");

                        if (!StringUtils.isEmpty(pacePointStr)) {
                            List<PacePoint> pacePointList = new ArrayList<>();
                            org.json.JSONArray objPace = new org.json.JSONArray(pacePointStr);
                            for (int i = 0; i < objPace.length(); i++) {
                                PacePoint pacepoint = new PacePoint(objPace.getJSONObject(i));
                                pacepoint.setWorkoutUuid(workout.getUuid());
                                pacePointList.add(pacepoint);
                            }

                            WorkoutDatabaseHelper.deletePacePoint("workoutuuid=?", new String[]{workout.getUuid()});
                            WorkoutDatabaseHelper.savePacePoints(pacePointList);
                        }


                        if (workout.getEndTime() <= workout2.getEndTime() && workout.getUploadStatus() == Enums.UploadStatus.Uploaded) {
                            workout2.setId(workout.getId());
                            if (TextUtils.isEmpty(workout2.getThumbnail())) {
                                workout2.setThumbnail(workout.getThumbnail());
                            }
                            workout2.setWorkStatus(Workout.STATUS_STOPED);//32
                            workout2.setFullData(true);
                            HistoryModelImpl.this.checkPacePointEmptyKM(workout2);
                            workout2.setFitPath(workout.getFitPath());
                            workout2.setEquipmentInfo(workout.getEquipmentInfo());
                            WorkoutDatabaseHelper.save(workout2);
                            workout2.setPointCounts(workout.getPointCounts());
                            workout.setFullData(true);
                            return Observable.just(workout2);
                        }
                        workout.setTrackSegments(workout2.getTrackSegments());
                        workout.setIsLike(workout2.isLike());
                        workout.setLikeCount(workout2.getLikeCount());
                        workout.setCommentCount(workout2.getCommentCount());
                        return Observable.just(workout);
                    } catch (JSONException e) {
                        e.printStackTrace();
                        return Observable.just(null);
                    }
                });
    }

    public void checkPacePointEmptyKM(Workout workout) throws JSONException {
        if (TextUtils.isEmpty(workout.getSegmentKM())) {
            return;
        }
        if (WorkoutDatabaseHelper.countPacePoint(workout.getUuid()) < new org.json.JSONArray(workout.getSegmentKM()).length()) {
            WorkoutDatabaseHelper.deletePacePoint("workoutuuid=?", new String[]{workout.getUuid()});
            workout.savePacePoint();
        }
        workout.setSegmentKM(null);
    }

    @Override
    public Observable<List<Trackpoint>> requestWorkoutPoint(Workout workout) {
//        MobclickAgent.onEventValue(App.getContext(), "history_download", null, 1);
        return requestPoints(workout, new ArrayList(), 0);
    }

    @Override
    public Observable<List<Trackpoint>> forceRequestWorkoutPoint(final Workout workout) {
        final ArrayList<Trackpoint> arrayList = new ArrayList<>();
        return Observable.create(new NetOnSubscribe.Builder(MyHttpClient.downloadAllPoints(workout.getUuid(), workout.getServerId())).build()).subscribeOn(Schedulers.io()).flatMap((Func1<Response, Observable<List<Trackpoint>>>) response -> {
            try {
                org.json.JSONArray jSONArray = new org.json.JSONArray(response.getBodyString());
                for (int i = 0; i < jSONArray.length(); i++) {
                    Trackpoint trackpoint = new Trackpoint(jSONArray.getJSONObject(i));
                    trackpoint.setWorkoutId(workout.getId());
                    arrayList.add(trackpoint);
                }
                return Observable.just(arrayList);
            } catch (IOException | JSONException e) {
                e.printStackTrace();
                return Observable.error(e);
            }
        }).subscribeOn(Schedulers.newThread()).map(list -> {
            WorkoutDatabaseHelper.saveTrackPoints(arrayList);
            return arrayList;
        });
    }

    private Observable<List<Trackpoint>> requestPoints(final Workout workout, final List<Trackpoint> list, final int i) {
        return Observable.create(new NetOnSubscribe.Builder(MyHttpClient.downloadAllPoints(workout.getUuid(), workout.getServerId())).build())
                .subscribeOn(Schedulers.io()).flatMap((Func1<Response, Observable<List<Trackpoint>>>) response -> {
                    try {
                        String bodyStr = response.getBodyString();
                        JSONObject obj = JSON.parseObject(bodyStr);
                        String code = obj.getString("code");
                        String message = obj.getString("msg");
                        String total = obj.getString("total");
                        String rows = obj.getString("rows");
                        Log.d("trackpoints", "code=" + code + "msg=" + message + "total=" + total);

                        org.json.JSONArray jSONArray = new org.json.JSONArray(rows);
                        Log.d("workouts", "get point length = " + jSONArray.length() + " , " + i);
                        for (int i2 = 0; i2 < jSONArray.length(); i2++) {
                            Trackpoint trackpoint = new Trackpoint(jSONArray.getJSONObject(i2));

                            //workoutId from server may be not correct
                            trackpoint.setWorkoutId(workout.getId());

                            list.add(trackpoint);
                        }
                        return Observable.just(list);
                    } catch (IOException | JSONException e) {
                        e.printStackTrace();
                        return Observable.error(e);
                    }
                }).subscribeOn(Schedulers.newThread()).map(list2 -> {
                    String localWorkoutId = String.valueOf(workout.getId());
                    LitePal.deleteAll(Trackpoint.class, "workoutId=?", localWorkoutId);
                    WorkoutDatabaseHelper.saveTrackPoints(list);
                    return list;
                });
    }

    public HashMap<String, List<Workout>> getWorkoutByYear(long minTime, long maxTime, long userId) {
        HashMap<String, List<Workout>> hashMap = new HashMap<>();
        // and startTime>=? and startTime<? and workStatus=32 todo sst need add back
        Cursor rawQuery = LitePal.getDatabase().rawQuery("select strftime('%m', starttime/1000,'unixepoch','localtime') as month, id, sport, uploadstatus, workstatus, serverid, title, hidden, distance, credits, cadencesource, heartsource, locsource, avgspeed, elevationgain, matchedsegments, threedworkout, likecount, commentcount, uuid, thumbnail, starttime, endtime, fulldata, synchronise, modifytime, uploadtime, category, slopegrademax,duration from workout where (userid=? or userid=0) and starttime>=? and starttime<? and workstatus=32 and (locsource<>2 or synchronise=1) order by starttime DESC", new String[]{String.valueOf(userId), String.valueOf(minTime), String.valueOf(maxTime)});//
        while (rawQuery.moveToNext()) {
            try {
                int index = rawQuery.getColumnIndex("month");

                String month = String.valueOf(rawQuery.getInt(index));
                List<Workout> list = hashMap.get(month);
                if (list == null) {
                    list = new LinkedList<>();
                    hashMap.put(month, list);
                }
                Workout workout = new Workout();
                index = rawQuery.getColumnIndex("id");
                workout.setId(rawQuery.getLong(index));
                index = rawQuery.getColumnIndex("sport");
                workout.setSport(rawQuery.getInt(index));
                index = rawQuery.getColumnIndex("uploadstatus");
                workout.setUploadStatus(rawQuery.getShort(index));
                index = rawQuery.getColumnIndex("serverid");
                workout.setServerId(rawQuery.getLong(index));
                index = rawQuery.getColumnIndex("title");
                workout.setTitle(rawQuery.getString(index));
                index = rawQuery.getColumnIndex("hidden");
                workout.setHidden(rawQuery.getInt(index));
                index = rawQuery.getColumnIndex("distance");
                workout.setDistance(rawQuery.getDouble(index));
                index = rawQuery.getColumnIndex("credits");
                workout.setCredits(rawQuery.getDouble(index));
                index = rawQuery.getColumnIndex("cadencesource");
                workout.setCadenceSource(rawQuery.getInt(index));
                index = rawQuery.getColumnIndex("heartsource");
                workout.setHeartSource(rawQuery.getInt(index));
                index = rawQuery.getColumnIndex("locsource");
                workout.setLocSource(rawQuery.getInt(index));
                index = rawQuery.getColumnIndex("avgspeed");
                workout.setAvgSpeed(rawQuery.getDouble(index));
                index = rawQuery.getColumnIndex("elevationgain");
                workout.setElevationGain(rawQuery.getFloat(index));
                index = rawQuery.getColumnIndex("matchedsegments");
                workout.setMatchedSegments(rawQuery.getInt(index));
                index = rawQuery.getColumnIndex("threedworkout");
                workout.setThreedWorkout(rawQuery.getString(index));
                index = rawQuery.getColumnIndex("likecount");
                workout.setLikeCount(rawQuery.getInt(index));
                index = rawQuery.getColumnIndex("commentcount");
                workout.setCommentCount(rawQuery.getInt(index));
                index = rawQuery.getColumnIndex("uuid");
                workout.setUuid(rawQuery.getString(index));
                index = rawQuery.getColumnIndex("thumbnail");
                workout.setThumbnail(rawQuery.getString(index));

                index = rawQuery.getColumnIndex("starttime");
                workout.setStartTime(rawQuery.getLong(index));
                index = rawQuery.getColumnIndex("endtime");
                workout.setEndTime(rawQuery.getLong(index));

                index = rawQuery.getColumnIndex("fulldata");
                workout.setFullData(rawQuery.getInt(index) != 0);

                workout.setUserId(userId);

                index = rawQuery.getColumnIndex("workstatus");
                workout.setWorkStatus(rawQuery.getInt(index));

                index = rawQuery.getColumnIndex("synchronise");
                workout.setSynchronise(rawQuery.getInt(index) != 0);
                index = rawQuery.getColumnIndex("modifytime");
                workout.setModifyTime(rawQuery.getLong(index));
                index = rawQuery.getColumnIndex("uploadtime");
                workout.setUploadTime(rawQuery.getLong(index));
                index = rawQuery.getColumnIndex("category");
                workout.setCategory(rawQuery.getInt(index));
                index = rawQuery.getColumnIndex("slopegrademax");
                if (rawQuery.getString(index) != null) {
                    workout.setSlopeGradeMax(rawQuery.getString(index));
                }
                index = rawQuery.getColumnIndex("duration");
                workout.setDuration(rawQuery.getLong(index));
                list.add(workout);

                hashMap.put(month, list);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }


        rawQuery.close();
        return hashMap;
    }

    public List<Integer> getLocalWorkoutYears(long userId) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        Cursor rawQuery = LitePal.getDatabase().rawQuery("select strftime('%Y', starttime/1000,'unixepoch','localtime') as year from workout where workstatus=32 and workout.userid=? and (locsource<>2 or synchronise=1)", new String[]{String.valueOf(userId)});
        while (rawQuery.moveToNext()) {
            arrayList.add(Integer.valueOf(rawQuery.getString(0)));
        }
        rawQuery.close();
        return arrayList;
    }

    public List<Integer> getUnUploadWorkoutYearsSync(long userId) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        Cursor rawQuery = LitePal.getDatabase().rawQuery("select strftime('%Y', starttime/1000,'unixepoch','localtime') as year from workout where workstatus=32 and workout.userid=? and uploadstatus<>2 and (locsource<>2 or synchronise=1)", new String[]{String.valueOf(userId)});
        while (rawQuery.moveToNext()) {
            arrayList.add(Integer.valueOf(rawQuery.getString(0)));
        }
        rawQuery.close();
        return arrayList;
    }

    @Override
    public Observable<List<Integer>> getUnUploadWorkoutYears(long userId) {
        return Observable.just(userId).subscribeOn(Schedulers.io()).flatMap((Func1<Long, Observable<List<Integer>>>) userId1 -> Observable.just(HistoryModelImpl.this.getUnUploadWorkoutYearsSync(userId1)));
    }

    @Override
    public Observable<Workout> hideWorkout(final Workout workout) {
        final int i = workout.getHidden() == 0 ? 1 : 0;
        return Observable.create(new NetSubscribe(MyHttpClient.hideWorkout(workout.getServerId(), i))).subscribeOn(Schedulers.io()).flatMap((Func1<String, Observable<Workout>>) s -> {
            workout.setHidden(i);
            workout.save();
            return Observable.just(workout);
        });
    }

    @Override
    public Observable<Workout> deleteWorkout(final Workout workout) {
        return Observable.create(new NetSubscribe(MyHttpClient.deleteWorkout(workout.getServerId(), 0))).subscribeOn(Schedulers.io()).flatMap((Func1<String, Observable<Workout>>) s -> {
            workout.delete();
            Trackpoint.deleteByWorkout(workout.getId());
            Segment.deleteByWorkout(workout.getId());
            return Observable.just(workout);
        });
    }


    public Calendar getUtcCalender() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(0L);
        calendar.add(Calendar.MILLISECOND, -(calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET)));

        return calendar;
    }

    @Override
    public void saveWorkout(final long workoutId, final int commentCount, final int likeCount) {
        Observable.just(workoutId).subscribeOn(Schedulers.io()).flatMap(id -> {
            Workout byId = Workout.getById(workoutId);
            if (byId != null) {
                byId.setCommentCount(commentCount);
                byId.setLikeCount(likeCount);
                byId.save();
            }
            return Observable.just(byId);
        }).observeOn(AndroidSchedulers.mainThread()).subscribe(workout -> {
        });
    }

    @Override
    public boolean isDownloaded(Workout workout) {
        if (workout == null) {
            return false;
        }
        long pointCounts = workout.getPointCounts();
        if (pointCounts == 0) {
            pointCounts = workout.getCountByWorkout();
        }
        return workout.isFullData() && pointCounts > 0;
    }

    @Override
    public Observable<ServerCodeJson> getHeatRecordForDate(String date) {
        return Observable.create(new NetSubscribe(MyHttpClient.getHeatRecordForDate(date))).subscribeOn(Schedulers.io()).map(s -> JSON.parseObject(s, ServerCodeJson.class));
    }

    @Override
    public Observable<ServerJson> hideMapWorkout(long workoutServerId, int hide) {
        return Observable.create(new NetSubscribe(MyHttpClient.hideMapWorkout(workoutServerId, hide))).subscribeOn(Schedulers.io()).map(s -> JSON.parseObject(s, ServerJson.class));
    }

    @Override
    public Observable<WorkoutSlopeBean> loadSlope(long workoutServerId) {
        //            static final /* synthetic */ boolean $assertionsDisabled = false;
        return Observable.create(new NetSubscribe(MyHttpClient.slopeInfo(workoutServerId))).subscribeOn(Schedulers.io()).map(s -> {
            ServerCodeJson serverCodeJson = JSON.parseObject(s, ServerCodeJson.class);
            if (serverCodeJson.getCode() == 200) {
                return JSON.parseObject(serverCodeJson.getData(), WorkoutSlopeBean.class);
            }
            Observable.error(new Throwable("slope is null"));
            return null;
        });
    }

    @Override
    public Observable<WorkoutSlopePointBean> loadSlopePoint(long workoutServerId, int type) {
        //            static final /* synthetic */ boolean $assertionsDisabled = false;
        return Observable.create(new NetSubscribe(MyHttpClient.slopePointsInfo(workoutServerId, type))).subscribeOn(Schedulers.io()).map(s -> {
            ServerCodeJson serverCodeJson = JSON.parseObject(s, ServerCodeJson.class);
            if (serverCodeJson.getCode() == 0) {
                return JSON.parseObject(serverCodeJson.getData(), WorkoutSlopePointBean.class);
            }
            Observable.error(new Throwable("slope point is null"));
            return null;
        });
    }
}